using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace tdgame {

    public struct VertexPositionNormalTextureColor : IVertexType {
        public Vector3 position;
        public Vector3 normal;
        private Vector2 texCoord;
        public Color color;

        public VertexPositionNormalTextureColor(Vector3 position, Vector3 normal, Color color) {
            this.position = position;
            this.normal = normal;
            this.texCoord = Vector2.Zero;
            this.color = color;
        }

        public readonly static VertexDeclaration vdecl = new VertexDeclaration(
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
            new VertexElement(sizeof(float) * 6, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
            new VertexElement(sizeof(float) * 8, VertexElementFormat.Color, VertexElementUsage.Color, 0));

        VertexDeclaration IVertexType.VertexDeclaration {
            get { return VertexPositionNormalTextureColor.vdecl; }
        }
    }

    public class Game1 : Microsoft.Xna.Framework.Game {
        Texture2D texture;
        private DeferredRenderer deferred;
        private BasicShader shader;

        int screenWidth = 1000,
			screenHeight = 700;
		bool fullScreen = false;

        WorldBuilder.MapSize mapSize = WorldBuilder.MapSize.Large;
        WorldBuilder.MapType mapType = WorldBuilder.MapType.Perlin;

        Vector2 lightRotation = new Vector2(260f, 290f);

		PolygonHelper polyHelper;




		bool fpsView = false;
        const float RAD = (float)Math.PI / 180.0f;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
		public Camera camera;

        public Vector3  lightDirectionNormal;

		List<VertexPositionNormalTextureColor> terrainVertices = new List<VertexPositionNormalTextureColor>(),
			waterVertices = new List<VertexPositionNormalTextureColor>(),
			gridVertices = new List<VertexPositionNormalTextureColor>(),
			overlayVertices = new List<VertexPositionNormalTextureColor>(),
			pathVertices = new List<VertexPositionNormalTextureColor>(),
			edgeDistVertices = new List<VertexPositionNormalTextureColor>();
        BasicEffect effect;
        KeyboardState lastKeyboardState;
        SpriteFont spriteFont;
        Point lastMousePos = new Point(0, 0);
        int lastMouseScroll = 0;
        WorldBuilder wb;
        double targetTime, targetCameraDist, targetAngle, targetDistDiff, targetAngleDiff;

		bool showOverlay = false;
		bool showGrid = false;
		bool showEdgeDist = false;

		VertexBuffer terrainBuffer, waterBuffer, gridBuffer, overlayBuffer, edgeBuffer;
		string mapGenTime;
		Pathfinder pf;
		

		double percentBuildable;
		float f = 0;
		private Model sphere;
		private Texture2D image;
		Lighting.DirectionalLight light;

        public Game1() {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferMultiSampling = true;
			graphics.PreferredBackBufferWidth = screenWidth;
			graphics.PreferredBackBufferHeight = screenHeight;
			graphics.IsFullScreen = fullScreen;

			
			//Window.AllowUserResizing = true;
			//Window.ClientSizeChanged += new EventHandler<EventArgs>(Window_ClientSizeChanged);
        }

		//void Window_ClientSizeChanged(object sender, EventArgs e) {
		//    graphics.PreferredBackBufferWidth = Window.ClientBounds.X;
		//    graphics.PreferredBackBufferHeight = Window.ClientBounds.Y;
		//    this.projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, this.GraphicsDevice.Viewport.AspectRatio, 1.0f, 5000.0f);

		//}

        protected override void Initialize() {

            this.IsMouseVisible = true;
            // TODO: Add your initialization logic here

			camera = new Camera(
				new Vector3(0, 100, 0),
				new Vector3(-90, 0, 0),
				1500f,
				this.GraphicsDevice.Viewport.AspectRatio);

			//camera = new Camera(new Vector3(0, 1000, 0),
			//    Vector3.Down,
			//    this.GraphicsDevice.Viewport.AspectRatio);

            targetAngle = -90;
            targetCameraDist = 1500f;
            targetTime = 0.0;
            lightDirectionNormal = new Vector3((float)Math.Cos(MathHelper.ToRadians(lightRotation.Y)), (float)Math.Sin(MathHelper.ToRadians(lightRotation.Y)), (float)Math.Cos(MathHelper.ToRadians(lightRotation.X)));
            lightDirectionNormal.Normalize();

            CreateWorld();

            this.effect = new BasicEffect(this.GraphicsDevice);

            base.Initialize();
        }

        protected override void LoadContent() {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            this.spriteFont = Content.Load<SpriteFont>("DefaultFont");

            this.deferred = new DeferredRenderer(this);
            this.deferred.SuperSampling = false;
			//this.deferred.SuperSampling = false;
            this.shader = new BasicShader(this.Content);
            this.shader.UseTexture = false;
            
            //this.shader.Parameters["Texture"].SetValue(this.texture);
            this.shader.DiffuseColor = new Vector3(1, 0, 0);

            light =  new Lighting.DirectionalLight(this);
			light.Direction = lightDirectionNormal; // new Vector3(0, -1, 0);
            light.Color = new Color(1.0f, 1.0f, 1.0f);
            light.Intensity = 1.0f;
            
            this.deferred.Lights.Add(light);


            // TODO: use this.Content to load your game content here
        }

        protected override void UnloadContent() {
            // TODO: Unload any non ContentManager content here
        }

        #region - world creator -

		void CreateWorld() {
			Random R = new Random();
			Stopwatch sw = new Stopwatch();

			sw.Start();

			//wb = new WorldBuilder(R.Next(10000), mapSize, mapType);
			wb = new WorldBuilder(755, mapSize, mapType);

			polyHelper = new PolygonHelper(ref wb);


			int buildable = 0, notBuildable = 0;

			terrainVertices = new List<VertexPositionNormalTextureColor>();
			waterVertices = new List<VertexPositionNormalTextureColor>();
			gridVertices = new List<VertexPositionNormalTextureColor>();
			overlayVertices = new List<VertexPositionNormalTextureColor>();
			pathVertices = new List<VertexPositionNormalTextureColor>();
            edgeDistVertices = new List<VertexPositionNormalTextureColor>();

			Color gridCol = Color.FromNonPremultiplied(0, 0, 0, 255);


			for (int x = 0; x < wb.map.GetUpperBound(0); x++) {
				for (int y = 0; y < wb.map.GetUpperBound(1); y++) {

					gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x, y], Vector3.Up, gridCol));
					gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x + 1, y], Vector3.Up, gridCol));

					gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x, y], Vector3.Up, gridCol));
					gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x, y + 1], Vector3.Up, gridCol));

					if (x == (wb.map.GetUpperBound(0) - 1) || y == (wb.map.GetUpperBound(1) - 1)) {
						gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x, y + 1], Vector3.Up, gridCol));
						gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x + 1, y + 1], Vector3.Up, gridCol));

						gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x + 1, y], Vector3.Up, gridCol));
						gridVertices.Add(new VertexPositionNormalTextureColor(wb.map[x + 1, y + 1], Vector3.Up, gridCol));
					}

					if (wb.cellMap[x, y].IsWater || wb.cellMap[x, y].IsSlope)
						notBuildable++;
					else
						buildable++;

					Color overlayCol =
						wb.cellMap[x, y].SlopeHeight <= 0.5f ? Color.FromNonPremultiplied(0, 200, 0, 150) :
						wb.cellMap[x, y].SlopeHeight <= 2 ? Color.FromNonPremultiplied(50, 50, 255, 150) :
						wb.cellMap[x, y].SlopeHeight <= 4 ? Color.FromNonPremultiplied(255, 127, 64, 150) :
							Color.FromNonPremultiplied(200, 0, 0, 150);

					if (!wb.cellMap[x, y].IsWater) {
						polyHelper.CreateCell(x, y, overlayCol, ref overlayVertices);
					}


					int edge = wb.cellMap[x, y].EdgeDistance;
					Color edgeCol;
					if (edge < 0)
						edge = 0;
					else if (edge > 12)
						edge = 12;
					edge = 25 - (edge * 2);
					edgeCol = Color.FromNonPremultiplied(edge * 10, edge * 10, edge * 10, 250);

					polyHelper.CreateCell(x, y, Color.Transparent, ref terrainVertices);

					polyHelper.CreateCell(x, y, edgeCol, ref edgeDistVertices);




					if (wb.cellMap[x, y].IsWater) {
						float level = 99.5f;
						waterVertices.Add(new VertexPositionNormalTextureColor(new Vector3(wb.map[x, y].X, level, wb.map[x, y].Z), Vector3.Up, Color.FromNonPremultiplied(0, 0, 255, 127)));
						waterVertices.Add(new VertexPositionNormalTextureColor(new Vector3(wb.map[x + 1, y].X, level, wb.map[x + 1, y].Z), Vector3.Up, Color.FromNonPremultiplied(0, 0, 255, 127)));
						waterVertices.Add(new VertexPositionNormalTextureColor(new Vector3(wb.map[x + 1, y + 1].X, level, wb.map[x + 1, y + 1].Z), Vector3.Up, Color.FromNonPremultiplied(0, 0, 255, 127)));


						waterVertices.Add(new VertexPositionNormalTextureColor(new Vector3(wb.map[x, y].X, level, wb.map[x, y].Z), Vector3.Up, Color.FromNonPremultiplied(0, 0, 255, 127)));
						waterVertices.Add(new VertexPositionNormalTextureColor(new Vector3(wb.map[x + 1, y + 1].X, level, wb.map[x + 1, y + 1].Z), Vector3.Up, Color.FromNonPremultiplied(0, 0, 255, 127)));
						waterVertices.Add(new VertexPositionNormalTextureColor(new Vector3(wb.map[x, y + 1].X, level, wb.map[x, y + 1].Z), Vector3.Up, Color.FromNonPremultiplied(0, 0, 255, 127)));
					}

					Random rand = new Random();

					if (rand.Next(1000) < 10 && !wb.cellMap[x, y].IsWater && wb.cellMap[x, y].SlopeHeight == 0) {
						//CreateTower(x, y);
                       
                    }
					
				}
			}

			percentBuildable = ((double)buildable / (double)(buildable + notBuildable)) * 100.0;


            try {
				List<WorldBuilder.Cell> cells = (from WorldBuilder.Cell c in wb.cellMap
												 where
													 c.BaseHeight > 102
													 && c.X > 3
													 && c.Z > 3
													 && c.X < (wb.cellMap.GetUpperBound(0) - 4)
													 && c.Z < (wb.cellMap.GetUpperBound(1) - 4)
												 select c).ToList();
                int maxspace = (from WorldBuilder.Cell c in cells select c.EdgeDistance).Max();
                cells = (from WorldBuilder.Cell c in cells where c.EdgeDistance == maxspace select c).ToList();

                int idx = R.Next(cells.Count);
                Point endPt = new Point(cells[idx].X, cells[idx].Z);
                pf = new Pathfinder(ref wb.cellMap, endPt);

                polyHelper.CreateTower(endPt.X, endPt.Y, ref terrainVertices);
            }
            catch { }

			terrainBuffer = new VertexBuffer(graphics.GraphicsDevice, VertexPositionNormalTextureColor.vdecl, terrainVertices.Count, BufferUsage.WriteOnly);
			terrainBuffer.SetData(terrainVertices.ToArray());

			if (overlayVertices.Count > 0) {
				overlayBuffer = new VertexBuffer(graphics.GraphicsDevice, VertexPositionNormalTextureColor.vdecl, overlayVertices.Count, BufferUsage.WriteOnly);
				overlayBuffer.SetData(overlayVertices.ToArray());
			}

			gridBuffer = new VertexBuffer(graphics.GraphicsDevice, VertexPositionNormalTextureColor.vdecl, gridVertices.Count, BufferUsage.WriteOnly);
			gridBuffer.SetData(gridVertices.ToArray());

			if (edgeDistVertices.Count > 0) {
				edgeBuffer = new VertexBuffer(graphics.GraphicsDevice, VertexPositionNormalTextureColor.vdecl, edgeDistVertices.Count, BufferUsage.WriteOnly);
				edgeBuffer.SetData(edgeDistVertices.ToArray());
			}

			if (waterVertices.Count > 0) {
				waterBuffer = new VertexBuffer(graphics.GraphicsDevice, VertexPositionNormalTextureColor.vdecl, waterVertices.Count, BufferUsage.WriteOnly);
				waterBuffer.SetData(waterVertices.ToArray());
			}

			sw.Stop();
			mapGenTime = "Map generation: " + Math.Round(sw.Elapsed.TotalSeconds, 3).ToString() + "s";


			OneOfEachPath();

		}

        Point GetStartPoint(Pathfinder.MovementRange moveRange) {
			bool valid = false;
			int max = wb.cellMap.GetUpperBound(0);
			Random R = new Random();
            Point ret = new Point();
			int tries = 0;
			while (!valid && tries < 10) {
				if (R.Next(2) == 0) { //horizontal edge
					if (pf.EndPointSector.X == 0)
                        ret.X = R.Next(max / 2, max);
					else
                        ret.X = R.Next(max / 2);

					if (pf.EndPointSector.Y == 0)
                        ret.Y = max - 1;
					else
                        ret.Y = 0;
				}
				else { //vertical edge
					if (pf.EndPointSector.Y == 0)
                        ret.Y = R.Next(max / 2, max);
					else
                        ret.Y = R.Next(max / 2);

					if (pf.EndPointSector.X == 0)
                        ret.X = max - 1;
					else
                        ret.X = 0;
				}

				valid = wb.cellMap[ret.X, ret.Y].Traversable(moveRange);
				tries++;
                //if (!wb.cellMap[ret.X, ret.Y].IsWater && wb.cellMap[ret.X, ret.Y].SlopeHeight <= 2) valid = true;
			}
            return ret;
        }

		void OneOfEachPath() {
			Stopwatch sw = new Stopwatch();

			Point endPt = pf.EndPoint;

			Stopwatch swHover = new Stopwatch(),
				swGround = new Stopwatch(),
				swCrawler = new Stopwatch(),
				swHeli = new Stopwatch(),
				swAir = new Stopwatch(),
				swWater = new Stopwatch(),
				swSub = new Stopwatch();
			int ctHover = 0,
				ctGround = 0,
				ctCrawler = 0,
				ctHeli = 0,
				ctAir = 0,
				ctWater = 0,
				ctSub = 0;


			List<Vector3> pts = new List<Vector3>();
			sw.Start();

			for (int i = 0; i < 4; i++) {

				swHover.Start();
				try {
					pts = pf.ComputeBestPath(GetStartPoint(Pathfinder.MovementRange.Hover), endPt, Pathfinder.MovementRange.Hover, 40);
					if (pts.Count > 0) {
						ctHover++;
						polyHelper.DrawPath(pts, Color.Magenta, ref pathVertices);
					}
				}
				catch { }
				swHover.Stop();

				swGround.Start();
				try {
					pts = pf.ComputeBestPath(GetStartPoint(Pathfinder.MovementRange.Ground), endPt, Pathfinder.MovementRange.Ground, 70);
					if (pts.Count > 0) {
						ctGround++;
						polyHelper.DrawPath(pts, Color.Yellow, ref pathVertices);
					}
				}
				catch { }
				swGround.Stop();

				swCrawler.Start();
				try {
					pts = pf.ComputeBestPath(GetStartPoint(Pathfinder.MovementRange.GroundCrawler), endPt, Pathfinder.MovementRange.GroundCrawler, 100);
					if (pts.Count > 0) {
						ctCrawler++;
						polyHelper.DrawPath(pts, Color.Cyan, ref pathVertices);
					}
				}
				catch { }
				swCrawler.Stop();

				swHeli.Start();
				try {
					pts = pf.ComputeBestPath(GetStartPoint(Pathfinder.MovementRange.AirHover), endPt, Pathfinder.MovementRange.AirHover, 120);
					if (pts.Count > 0) {
						ctHeli++;
						polyHelper.DrawPath(pts, Color.Red, ref pathVertices);
					}
				}
				catch { }
				swHeli.Stop();

				swAir.Start();
				try {
					pts = pf.ComputeBestPath(GetStartPoint(Pathfinder.MovementRange.Air), endPt, Pathfinder.MovementRange.Air, 140);
					if (pts.Count > 0) {
						ctAir++;
						polyHelper.DrawPath(pts, Color.Orange, ref pathVertices);
					}
				}
				catch { }
				swAir.Stop();

				swWater.Start();
				try {
					pts = pf.ComputeBestPath(GetStartPoint(Pathfinder.MovementRange.WaterSurface), endPt, Pathfinder.MovementRange.WaterSurface, 220);
					if (pts.Count > 0) {
						ctWater++;
						polyHelper.DrawPath(pts, Color.Green, ref pathVertices);
					}
				}
				catch { }
				swWater.Stop();

				swSub.Start();
				try {
					pts = pf.ComputeBestPath(GetStartPoint(Pathfinder.MovementRange.WaterSubmerged), endPt, Pathfinder.MovementRange.WaterSubmerged, 250);
					if (pts.Count > 0) {
						ctSub++;
						polyHelper.DrawPath(pts, Color.White, ref pathVertices);
					}
				}
				catch { }
				swSub.Stop();
			}

			mapGenTime += "\nTotal PF time: " + Math.Round(sw.Elapsed.TotalSeconds, 3).ToString();
			if (ctSub > 0) mapGenTime +=		String.Format("\nsub    ({1}): {0}", Math.Round(swSub.Elapsed.TotalSeconds, 3).ToString(), ctSub);
			if (ctWater > 0) mapGenTime +=		String.Format("\nwater  ({1}): {0}", Math.Round(swWater.Elapsed.TotalSeconds, 3).ToString(), ctWater);
			if (ctHover > 0) mapGenTime +=		String.Format("\nhover  ({1}): {0}", Math.Round(swHover.Elapsed.TotalSeconds, 3).ToString(), ctHover);
			if (ctGround > 0) mapGenTime +=		String.Format("\ntank   ({1}): {0}", Math.Round(swGround.Elapsed.TotalSeconds, 3).ToString(), ctGround);
			if (ctCrawler > 0) mapGenTime +=	String.Format("\ncrawl  ({1}): {0}", Math.Round(swCrawler.Elapsed.TotalSeconds, 3).ToString(), ctCrawler);
			if (ctHeli > 0) mapGenTime +=		String.Format("\nheli   ({1}): {0}", Math.Round(swHeli.Elapsed.TotalSeconds, 3).ToString(), ctHeli);
			if (ctAir > 0) mapGenTime +=		String.Format("\nair    ({1}): {0}", Math.Round(swAir.Elapsed.TotalSeconds, 3).ToString(), ctAir);
		}

        #endregion


        protected override void Update(GameTime gameTime) {
            KeyboardState state = Keyboard.GetState();
            MouseState mouse = Mouse.GetState();
			
                Vector3 cameraTranslation = Vector3.Zero;
				Vector3 frameRotation = Vector3.Zero;

            if (this.IsActive) {


                if (mouse.MiddleButton == ButtonState.Pressed) {
					cameraTranslation.X += (float)Math.Cos(camera.Rotation.Y * RAD) * (lastMousePos.X - mouse.X);
					cameraTranslation.Z -= (float)Math.Sin(camera.Rotation.Y * RAD) * (lastMousePos.X - mouse.X);

					cameraTranslation.X += (float)Math.Cos((camera.Rotation.Y - 90) * RAD) * (lastMousePos.Y - mouse.Y);
					cameraTranslation.Z -= (float)Math.Sin((camera.Rotation.Y - 90) * RAD) * (lastMousePos.Y - mouse.Y);

                    cameraTranslation *= camera.TargetDistance / 10f;
                }
				else if (mouse.RightButton == ButtonState.Pressed) {
					frameRotation.X -= (mouse.Y - lastMousePos.Y);
					frameRotation.Y -= (mouse.X - lastMousePos.X);
				}


                if (state.IsKeyDown(Keys.C)) {
					camera = new Camera(
						new Vector3(500, 100, 500),
						new Vector3(-90, 0, 0),
						1500f,
						this.GraphicsDevice.Viewport.AspectRatio);

                }

				float moveFactor = 30.0f;
				if (state.IsKeyDown(Keys.A)) {
					cameraTranslation.X -= (float)Math.Cos(camera.Rotation.Y * RAD) * moveFactor;
					cameraTranslation.Z += (float)Math.Sin(camera.Rotation.Y * RAD) * moveFactor;
				}
				else if (state.IsKeyDown(Keys.D)) {
					cameraTranslation.X += (float)Math.Cos(camera.Rotation.Y * RAD) * moveFactor;
					cameraTranslation.Z -= (float)Math.Sin(camera.Rotation.Y * RAD) * moveFactor;
				}

				if (state.IsKeyDown(Keys.W)) {
					cameraTranslation.X -= (float)Math.Cos((camera.Rotation.Y - 90) * RAD) * moveFactor;
					cameraTranslation.Z += (float)Math.Sin((camera.Rotation.Y - 90) * RAD) * moveFactor;
				}
				else if (state.IsKeyDown(Keys.S)) {
					cameraTranslation.X += (float)Math.Cos((camera.Rotation.Y - 90) * RAD) * moveFactor;
					cameraTranslation.Z -= (float)Math.Sin((camera.Rotation.Y - 90) * RAD) * moveFactor;
				}

				if (state.IsKeyDown(Keys.Space)) {
					cameraTranslation.Y += moveFactor;
				}
				else if (state.IsKeyDown(Keys.LeftShift)) {
					cameraTranslation.Y -= moveFactor;
				}


                bool updateLight = false;
                if (state.IsKeyDown(Keys.I)) {
                    lightRotation.Y += 1f;
                    if (lightRotation.Y >= 360f)
                        lightRotation.Y = 0f;
                    updateLight = true;
                }
                if (state.IsKeyDown(Keys.K)) {
                    lightRotation.Y -= 1f;
                    if (lightRotation.Y < 0f)
                        lightRotation.Y = 359f;
                    updateLight = true;
                }
                if (state.IsKeyDown(Keys.J)) {
                    lightRotation.X -= 1f;
                    if (lightRotation.X < 0f)
                        lightRotation.X = 360f;
                    updateLight = true;
                }
                if (state.IsKeyDown(Keys.L)) {
                    lightRotation.X += 1f;
                    if (lightRotation.X >= 360f)
                        lightRotation.X = 0f;
                    updateLight = true;
                }
                if (state.IsKeyDown(Keys.T)) {
                    targetCameraDist = 100f;
                    targetAngle = -20f;
                    targetTime = 0.5;

                    targetDistDiff = targetCameraDist - camera.TargetDistance;
                    targetAngleDiff = targetAngle - camera.Rotation.X;

                }
                if (state.IsKeyDown(Keys.Y)) {
                    targetCameraDist = 1500f;
                    targetAngle = -90f;
                    targetTime = 0.5;

					targetDistDiff = targetCameraDist - camera.TargetDistance;
					targetAngleDiff = targetAngle - camera.Rotation.X;

                }

                if (state.IsKeyDown(Keys.Escape)) {
                    Exit();
                }

                if (state.IsKeyDown(Keys.R) && lastKeyboardState.IsKeyUp(Keys.R)) {
                    CreateWorld();
                }

				if (state.IsKeyDown(Keys.G) && lastKeyboardState.IsKeyUp(Keys.G)) {
					showGrid = !showGrid;
				}

				if (state.IsKeyDown(Keys.O) && lastKeyboardState.IsKeyUp(Keys.O)) {
					showOverlay = !showOverlay;
				}

				if (state.IsKeyDown(Keys.E) && lastKeyboardState.IsKeyUp(Keys.E)) {
					showEdgeDist = !showEdgeDist;
				}

				if (state.IsKeyDown(Keys.Z) && lastKeyboardState.IsKeyUp(Keys.Z)) {
					deferred.SuperSampling = !deferred.SuperSampling;
				}
				if (state.IsKeyDown(Keys.F) && lastKeyboardState.IsKeyUp(Keys.F)) {
					camera.FPSView = !camera.FPSView;
				}

                if (updateLight) {
                    lightDirectionNormal = new Vector3((float)Math.Cos(MathHelper.ToRadians(lightRotation.Y)), (float)Math.Sin(MathHelper.ToRadians(lightRotation.Y)), (float)Math.Cos(MathHelper.ToRadians(lightRotation.X)));
                    lightDirectionNormal.Normalize();
                }



                int scroll = (mouse.ScrollWheelValue - lastMouseScroll) / 120;
                if (scroll > 0 && targetCameraDist > 100f) {
                    targetCameraDist -= 100f;
                    targetAngle += 5f;
                    targetTime = 0.5;

                    targetDistDiff = targetCameraDist - camera.TargetDistance;
					targetAngleDiff = targetAngle - camera.Rotation.X;
                }
                else if (scroll < 0 && targetCameraDist < 1500f) {
                    targetCameraDist += 100f;
                    targetAngle -= 5f;
                    targetTime = 0.5;

					targetDistDiff = targetCameraDist - camera.TargetDistance;
					targetAngleDiff = targetAngle - camera.Rotation.X;
                }

                lastMousePos = new Point(mouse.X, mouse.Y);
                lastMouseScroll = mouse.ScrollWheelValue;


				cameraTranslation *= (float)gameTime.ElapsedGameTime.TotalSeconds;

				float newDistance = camera.TargetDistance;
                if (targetTime > 0.0) {
                    targetTime -= gameTime.ElapsedGameTime.TotalSeconds;
                    double ratio = gameTime.ElapsedGameTime.TotalSeconds / 0.5;

					newDistance += (float)(ratio * targetDistDiff);

					frameRotation.X += (float)(ratio * targetAngleDiff);
                }

				camera.Update(cameraTranslation, frameRotation, newDistance);
            }

            lastKeyboardState = state;

            base.Update(gameTime);

        }

        protected override void Draw(GameTime gameTime) {
            this.shader.World = camera.World;
			this.shader.View = camera.View;
			this.shader.Projection = camera.Projection;

            this.light.Direction = lightDirectionNormal;
            this.deferred.Begin();

			this.shader.Parameters["specularIntensity"].SetValue(0.0f);
            this.shader.Techniques[0].Passes[0].Apply();

            this.GraphicsDevice.SetVertexBuffer(terrainBuffer);
            this.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, terrainVertices.Count / 3);

            this.shader.World = Matrix.CreateTranslation(0, 0, 0);
            this.shader.Techniques[0].Passes[0].Apply();
            if (showGrid)
            {
                this.GraphicsDevice.SetVertexBuffer(gridBuffer);
                this.GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, gridVertices.Count / 2);
            }

            if (showOverlay && overlayVertices.Count>0)
            {
                this.GraphicsDevice.SetVertexBuffer(overlayBuffer);
                this.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, overlayVertices.Count / 3);
            }

            if (showEdgeDist && edgeDistVertices.Count > 0)
            {
                this.GraphicsDevice.SetVertexBuffer(edgeBuffer);
                this.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, edgeDistVertices.Count / 3);
            }

            if (waterVertices.Count > 0)
            {
				this.shader.Parameters["specularIntensity"].SetValue(0.5f);
				this.shader.Techniques[0].Passes[0].Apply();
                
				this.GraphicsDevice.SetVertexBuffer(waterBuffer);
                this.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, waterVertices.Count / 3);
            }

            if (pathVertices.Count > 0)
            {
				this.shader.Parameters["specularIntensity"].SetValue(0.0f);
				this.shader.Techniques[0].Passes[0].Apply();
				
				this.GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTextureColor>(
                    PrimitiveType.LineList, pathVertices.ToArray(), 0, pathVertices.Count / 2);
            }

            this.deferred.End();

            this.spriteBatch.Begin();
            this.spriteBatch.DrawString(
                this.spriteFont,
				string.Format("FPS: {1}" +
                        "\n{5}" +
						"\n{7}", 
					this.camera.Position, //0
					Math.Round(1000.0 / gameTime.ElapsedGameTime.Milliseconds, 1).ToString(), //1
					this.camera.Rotation, //2
					this.camera.TargetDistance, //3
					lightRotation, //4
					mapGenTime, //5
					Math.Round(percentBuildable, 2).ToString() + "% buildable", //6
					wb.MapSeed, //7
					pf.EndPointSector //8
				),
                new Vector2(3, 3),
                Color.White);
			
            this.spriteBatch.End();
            
            base.Draw(gameTime);
        }
    }
}
